home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / src / stacking.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-29  |  16.2 KB  |  607 lines

  1. /*
  2.  *  Window Maker window manager
  3.  * 
  4.  *  Copyright (c) 1997, 1998 Alfredo K. Kojima
  5.  *  Copyright (c) 1998       Dan Pascu
  6.  * 
  7.  *  This program is free software; you can redistribute it and/or modify
  8.  *  it under the terms of the GNU General Public License as published by
  9.  *  the Free Software Foundation; either version 2 of the License, or
  10.  *  (at your option) any later version.
  11.  *
  12.  *  This program is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *  GNU General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU General Public License
  18.  *  along with this program; if not, write to the Free Software
  19.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
  20.  *  USA.
  21.  */
  22.  
  23. #include "wconfig.h"
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <X11/Xlib.h>
  29. #include <X11/Xutil.h>
  30.  
  31. #include "WindowMaker.h"
  32. #include "screen.h"
  33. #include "window.h"
  34. #include "funcs.h"
  35. #include "actions.h"
  36. #include "properties.h"
  37. #include "stacking.h"
  38. #ifdef KWM_HINTS
  39. #include "kwm.h"
  40. #endif
  41.  
  42. /*** Global Variables ***/
  43. extern XContext wStackContext;
  44.  
  45. extern WPreferences wPreferences;
  46.  
  47.  
  48. /*
  49.  *---------------------------------------------------------------------- 
  50.  * RemakeStackList--
  51.  *     Remakes the stacking_list for the screen, getting the real
  52.  * stacking order from the server and reordering windows that are not
  53.  * in the correct stacking.
  54.  * 
  55.  * Side effects:
  56.  *     The stacking order list and the actual window stacking 
  57.  * may be changed (corrected)
  58.  * 
  59.  *---------------------------------------------------------------------- 
  60.  */
  61. void
  62. RemakeStackList(WScreen *scr)
  63. {
  64.     Window *windows;
  65.     unsigned int nwindows;
  66.     Window junkr, junkp;
  67.     WCoreWindow *frame;
  68.     WCoreWindow *tmp;
  69.     int level;
  70.     int i, c;
  71.     
  72.     if (!XQueryTree(dpy, scr->root_win, &junkr, &junkp, &windows, &nwindows)) {
  73.     wwarning(_("could not get window list!!"));
  74.     return;
  75.     } else {
  76.     WMEmptyBag(scr->stacking_list);
  77.  
  78.     /* verify list integrity */
  79.     c=0;
  80.     for (i=0; i<nwindows; i++) {
  81.         if (XFindContext(dpy, windows[i], wStackContext, (XPointer*)&frame)
  82.         ==XCNOENT) {
  83.         continue;
  84.         }
  85.         if (!frame) continue;
  86.         c++;
  87.         level = frame->stacking->window_level;
  88.         tmp = WMGetFromBag(scr->stacking_list, level);
  89.         if (tmp)
  90.         tmp->stacking->above = frame;
  91.         frame->stacking->under = tmp;
  92.         frame->stacking->above = NULL;
  93.         WMSetInBag(scr->stacking_list, level, frame);
  94.         }
  95.         XFree(windows);
  96. #ifdef DEBUG
  97.     if (c!=scr->window_count) {
  98.         puts("Found different number of windows than in window lists!!!");
  99.     }
  100. #endif
  101.     scr->window_count = c;
  102.     }    
  103.     
  104.     CommitStacking(scr);
  105. }
  106.  
  107.  
  108.  
  109. /*
  110.  *----------------------------------------------------------------------
  111.  * CommitStacking--
  112.  *     Reorders the actual window stacking, so that it has the stacking
  113.  * order in the internal window stacking lists. It does the opposite
  114.  * of RemakeStackList().
  115.  * 
  116.  * Side effects:
  117.  *     Windows may be restacked.
  118.  *---------------------------------------------------------------------- 
  119.  */
  120. void
  121. CommitStacking(WScreen *scr)
  122. {
  123.     WCoreWindow *tmp;
  124.     int nwindows, i;
  125.     Window *windows;
  126.     WMBagIterator iter;
  127.     
  128.     nwindows = scr->window_count;
  129.     windows = wmalloc(sizeof(Window)*nwindows);
  130.        
  131.     i = 0;
  132.     WM_ETARETI_BAG(scr->stacking_list, tmp, iter) {
  133.     while (tmp) {
  134. #ifdef DEBUG
  135.         if (i>=nwindows) {
  136.         puts("Internal inconsistency! window_count is incorrect!!!");
  137.         printf("window_count says %i windows\n", nwindows);
  138.         free(windows);
  139.         return;
  140.         }
  141. #endif
  142.         windows[i++] = tmp->window;
  143.         tmp = tmp->stacking->under;
  144.     }
  145.     }
  146.     XRestackWindows(dpy, windows, i);
  147.     free(windows);
  148.     
  149. #ifdef KWM_HINTS
  150.     wKWMBroadcastStacking(scr);
  151. #endif
  152.     
  153. }
  154.  
  155. /*
  156.  *----------------------------------------------------------------------
  157.  * moveFrameToUnder--
  158.  *     Reestacks windows so that "frame" is under "under".
  159.  * 
  160.  * Returns:
  161.  *    None
  162.  * 
  163.  * Side effects:
  164.  *     Changes the stacking order of frame.
  165.  *----------------------------------------------------------------------
  166.  */
  167. static void
  168. moveFrameToUnder(WCoreWindow *under, WCoreWindow *frame)
  169. {
  170.     Window wins[2];
  171.  
  172.     wins[0] = under->window;
  173.     wins[1] = frame->window;
  174.     XRestackWindows(dpy, wins, 2);
  175.  
  176. #ifdef KWM_HINTS
  177.     wKWMBroadcastStacking(under->screen_ptr);
  178. #endif
  179. }
  180.  
  181. /*
  182.  *----------------------------------------------------------------------
  183.  * wRaiseFrame--
  184.  *     Raises a frame taking the window level into account. 
  185.  * 
  186.  * Returns:
  187.  *     None
  188.  * 
  189.  * Side effects:
  190.  *     Window stacking order and stacking list are changed.
  191.  * 
  192.  *---------------------------------------------------------------------- 
  193.  */
  194. void 
  195. wRaiseFrame(WCoreWindow *frame)
  196. {
  197.     WCoreWindow *wlist = frame;
  198.     int level = frame->stacking->window_level;
  199.     WScreen *scr = frame->screen_ptr;
  200.  
  201.     /* already on top */
  202.     if (frame->stacking->above == NULL) {
  203.     return;
  204.     }
  205.  
  206.     /* insert it on top of other windows on the same level */
  207.     if (frame->stacking->under)
  208.     frame->stacking->under->stacking->above = frame->stacking->above;
  209.     if (frame->stacking->above)
  210.     frame->stacking->above->stacking->under = frame->stacking->under;
  211.     
  212.     frame->stacking->above = NULL;
  213.     frame->stacking->under = WMGetFromBag(scr->stacking_list, level);
  214.     frame->stacking->under->stacking->above = frame;
  215.     WMSetInBag(scr->stacking_list, level, frame);
  216.  
  217.     /* raise transients under us from bottom to top 
  218.      * so that the order is kept */
  219. again:
  220.     wlist = frame->stacking->under;
  221.     while (wlist && wlist->stacking->under)
  222.     wlist = wlist->stacking->under;
  223.     while (wlist && wlist!=frame) {
  224.     if (wlist->stacking->child_of == frame) {
  225.         wRaiseFrame(wlist);
  226.         goto again;
  227.     }
  228.     wlist = wlist->stacking->above;
  229.     }
  230.  
  231.     /* try to optimize things a little */
  232.     if (frame->stacking->above == NULL) {
  233.     WMBagIterator iter;
  234.     WCoreWindow *above = WMBagLast(scr->stacking_list, &iter);
  235.     int i, last = above->stacking->window_level;
  236.  
  237.     /* find the 1st level above us which has windows in it */
  238.     for (i = level+1, above = NULL; i <= last; i++) {
  239.         above = WMGetFromBag(scr->stacking_list, i);
  240.         if (above != NULL)
  241.         break;
  242.     }
  243.  
  244.     if (above != frame && above != NULL) {
  245.         while (above->stacking->under)
  246.         above = above->stacking->under;
  247.         moveFrameToUnder(above, frame);
  248.     } else {
  249.         /* no window above us */
  250.         above = NULL;
  251.         XRaiseWindow(dpy, frame->window);
  252.     }
  253.     } else {
  254.     moveFrameToUnder(frame->stacking->above, frame);
  255.     }
  256. #ifdef KWM_HINTS
  257.     {
  258.     WWindow *wwin = wWindowFor(frame->window);
  259.  
  260.     if (wwin != NULL)
  261.         wKWMSendEventMessage(wwin, WKWMRaiseWindow);
  262.     }
  263. #endif
  264. #ifdef VIRTUAL_DESKTOP
  265.     wWorkspaceRaiseEdge(scr);
  266. #endif
  267.     
  268. }
  269.  
  270.  
  271. void
  272. wRaiseLowerFrame(WCoreWindow *frame)
  273. {
  274.     if (!frame->stacking->above
  275.     ||(frame->stacking->window_level
  276.        !=frame->stacking->above->stacking->window_level)) {
  277.  
  278.     wLowerFrame(frame);
  279.     } else {
  280.     WCoreWindow *scan = frame->stacking->above;
  281.     WWindow *frame_wwin = (WWindow*) frame->descriptor.parent;
  282.  
  283.     while (scan) {
  284.  
  285.         if (scan->descriptor.parent_type == WCLASS_WINDOW) {
  286.         WWindow *scan_wwin = (WWindow*)scan->descriptor.parent;
  287.         
  288.         if (wWindowObscuresWindow(scan_wwin, frame_wwin)
  289.             && scan_wwin->flags.mapped) {
  290.             break;
  291.         }
  292.         }
  293.         scan = scan->stacking->above;
  294.     }
  295.  
  296.     if (scan) {
  297.         wRaiseFrame(frame);
  298.     } else {
  299.         wLowerFrame(frame);
  300.     }
  301.     }
  302. }
  303.  
  304.  
  305. void 
  306. wLowerFrame(WCoreWindow *frame)
  307. {
  308.     WScreen *scr = frame->screen_ptr;
  309.     WCoreWindow *wlist=frame;
  310.     int level = frame->stacking->window_level;
  311.  
  312.     /* already in bottom */
  313.     if (wlist->stacking->under == NULL) {
  314.     return;
  315.     }
  316.     /* cant lower transient below below its owner */
  317.     if (wlist->stacking->under == wlist->stacking->child_of) {
  318.     return;
  319.     }
  320.     /* remove from the list */
  321.     if (WMGetFromBag(scr->stacking_list, level) == frame) {
  322.     /* it was the top window */
  323.     WMSetInBag(scr->stacking_list, level, frame->stacking->under);
  324.     frame->stacking->under->stacking->above = NULL;
  325.     } else {
  326.     if (frame->stacking->under)
  327.         frame->stacking->under->stacking->above = frame->stacking->above;
  328.     if (frame->stacking->above)
  329.         frame->stacking->above->stacking->under = frame->stacking->under;
  330.     }
  331.     wlist = WMGetFromBag(scr->stacking_list, level);
  332.  
  333.     /* look for place to put this window */
  334.     {
  335.     WCoreWindow *owner = frame->stacking->child_of;
  336.     
  337.     if (owner != wlist) {
  338.         while (wlist->stacking->under) {
  339.         /* if this is a transient, it should not be placed under 
  340.          * it's owner */
  341.         if (owner == wlist->stacking->under)
  342.             break;
  343.         wlist = wlist->stacking->under;
  344.         }
  345.     }
  346.     } 
  347.     /* insert under the place found */
  348.     frame->stacking->above = wlist;
  349.     frame->stacking->under = wlist->stacking->under;
  350.     if (wlist->stacking->under)
  351.     wlist->stacking->under->stacking->above = frame;
  352.     wlist->stacking->under = frame;
  353.  
  354.     if (frame->stacking->above == NULL) {
  355.     WMBagIterator iter;
  356.     WCoreWindow *above = WMBagLast(scr->stacking_list, &iter);
  357.     int i, last = above->stacking->window_level;
  358.  
  359.     /* find the 1st level above us which has windows in it */
  360.     for (i = level+1, above = NULL; i <= last; i++) {
  361.         above = WMGetFromBag(scr->stacking_list, i);
  362.         if (above != NULL)
  363.         break;
  364.     }
  365.  
  366.     if (above != frame && above != NULL) {
  367.         while (above->stacking->under)
  368.         above = above->stacking->under;
  369.         moveFrameToUnder(above, frame);
  370.     } else {
  371.         /* no window below us */
  372.         XLowerWindow(dpy, frame->window);
  373.     }
  374.     } else {
  375.     moveFrameToUnder(frame->stacking->above, frame);
  376.     }
  377. #ifdef KWM_HINTS
  378.     {
  379.     WWindow *wwin = wWindowFor(frame->window);
  380.  
  381.     if (wwin)
  382.         wKWMSendEventMessage(wwin, WKWMLowerWindow);
  383.     }
  384. #endif
  385.         
  386. }
  387.  
  388.  
  389. /*
  390.  *----------------------------------------------------------------------
  391.  * AddToStackList--
  392.  *     Inserts the frame in the top of the stacking list. The
  393.  * stacking precedence is obeyed.
  394.  * 
  395.  * Returns:
  396.  *     None
  397.  * 
  398.  * Side effects:
  399.  *     The frame is added to it's screen's window list.
  400.  *---------------------------------------------------------------------- 
  401.  */
  402. void
  403. AddToStackList(WCoreWindow *frame)
  404. {
  405.     WCoreWindow *curtop, *wlist;
  406.     int index = frame->stacking->window_level;
  407.     WScreen *scr = frame->screen_ptr;
  408.     WCoreWindow *trans = NULL;
  409.     
  410.     frame->screen_ptr->window_count++;
  411.     XSaveContext(dpy, frame->window, wStackContext, (XPointer)frame);
  412.     curtop = WMGetFromBag(scr->stacking_list, index);
  413.  
  414.     /* first window in this level */
  415.     if (curtop == NULL) {
  416.     WMSetInBag(scr->stacking_list, index, frame);
  417.     frame->stacking->above = NULL;
  418.     frame->stacking->under = NULL;
  419.     CommitStacking(scr);
  420.     return;
  421.     }
  422.  
  423.     /* check if this is a transient owner */
  424.     wlist = curtop;
  425.     while (wlist) {
  426.     if (wlist->stacking->child_of == frame)
  427.         trans = wlist;
  428.     wlist = wlist->stacking->under;
  429.     }
  430.     /* trans will hold the transient in the lowest position
  431.      * in stacking list */
  432.     
  433.     frame->stacking->above = trans;
  434.     if (trans != NULL) {
  435.     /* window is owner of a transient.. put it below
  436.      * the lowest transient */
  437.     frame->stacking->under = trans->stacking->under;
  438.     if (trans->stacking->under) {
  439.         trans->stacking->under->stacking->above = frame;
  440.     }
  441.     trans->stacking->under = frame;
  442.     } else {
  443.     /* window is not owner of transients.. just put it in the
  444.      * top of other windows */
  445.     frame->stacking->under = curtop;
  446.     curtop->stacking->above = frame;
  447.     WMSetInBag(scr->stacking_list, index, frame);
  448.     }
  449.     CommitStacking(scr);
  450.         
  451. }
  452.  
  453.  
  454. /*
  455.  *----------------------------------------------------------------------
  456.  * MoveInStackListAbove--
  457.  *     Moves the frame above "next".
  458.  * 
  459.  * Returns:
  460.  *     None
  461.  * 
  462.  * Side effects:
  463.  *     Stacking order may be changed.
  464.  *      Window level for frame may be changed.
  465.  *---------------------------------------------------------------------- 
  466.  */
  467. void
  468. MoveInStackListAbove(WCoreWindow *next, WCoreWindow *frame)
  469. {
  470.     WCoreWindow *tmpw;
  471.     WScreen *scr = frame->screen_ptr;
  472.     int index;
  473.  
  474.     if (!next || frame->stacking->under == next)
  475.         return;
  476.  
  477.     if (frame->stacking->window_level != next->stacking->window_level)
  478.         ChangeStackingLevel(frame, next->stacking->window_level);
  479.  
  480.     index = frame->stacking->window_level;
  481.  
  482.     tmpw = WMGetFromBag(scr->stacking_list, index);
  483.     if (tmpw == frame)
  484.     WMSetInBag(scr->stacking_list, index, frame->stacking->under);
  485.     if (frame->stacking->under)
  486.         frame->stacking->under->stacking->above = frame->stacking->above;
  487.     if (frame->stacking->above)
  488.         frame->stacking->above->stacking->under = frame->stacking->under;
  489.     if (next->stacking->above)
  490.         next->stacking->above->stacking->under = frame;
  491.     frame->stacking->under = next;
  492.     frame->stacking->above = next->stacking->above;
  493.     next->stacking->above = frame;
  494.     if (tmpw == next)
  495.     WMSetInBag(scr->stacking_list, index, frame);
  496.  
  497.     /* try to optimize things a little */
  498.     if (frame->stacking->above == NULL) {
  499.         WCoreWindow *above = NULL;
  500.     WMBagIterator iter;
  501.  
  502.     for (above = WMBagIteratorAtIndex(scr->stacking_list, index+1, &iter);
  503.          above != NULL;
  504.          above = WMBagNext(scr->stacking_list, &iter)) {
  505.  
  506.         /* can't optimize */
  507.         while (above->stacking->under)
  508.         above = above->stacking->under;
  509.         break;
  510.         }
  511.         if (above == NULL) {
  512.             XRaiseWindow(dpy, frame->window);
  513.         } else {
  514.             moveFrameToUnder(above, frame);
  515.         }
  516.     } else {
  517.         moveFrameToUnder(frame->stacking->above, frame);
  518.     }
  519.         
  520. }
  521.  
  522.  
  523. /*
  524.  *----------------------------------------------------------------------
  525.  * MoveInStackListUnder--
  526.  *     Moves the frame to under "prev".
  527.  * 
  528.  * Returns:
  529.  *     None
  530.  * 
  531.  * Side effects:
  532.  *     Stacking order may be changed.
  533.  *      Window level for frame may be changed.
  534.  *---------------------------------------------------------------------- 
  535.  */
  536. void
  537. MoveInStackListUnder(WCoreWindow *prev, WCoreWindow *frame)
  538. {
  539.     WCoreWindow *tmpw;
  540.     int index;
  541.     WScreen *scr = frame->screen_ptr;
  542.  
  543.     if (!prev || frame->stacking->above == prev)
  544.         return;
  545.  
  546.     if (frame->stacking->window_level != prev->stacking->window_level)
  547.         ChangeStackingLevel(frame, prev->stacking->window_level);
  548.  
  549.     index = frame->stacking->window_level;
  550.  
  551.     tmpw = WMGetFromBag(scr->stacking_list, index);
  552.     if (tmpw == frame)
  553.         WMSetInBag(scr->stacking_list, index, frame->stacking->under);
  554.     if (frame->stacking->under)
  555.         frame->stacking->under->stacking->above = frame->stacking->above;
  556.     if (frame->stacking->above)
  557.         frame->stacking->above->stacking->under = frame->stacking->under;
  558.     if (prev->stacking->under)
  559.         prev->stacking->under->stacking->above = frame;
  560.     frame->stacking->above = prev;
  561.     frame->stacking->under = prev->stacking->under;
  562.     prev->stacking->under = frame;
  563.     moveFrameToUnder(prev, frame);
  564. }
  565.  
  566.  
  567. void
  568. RemoveFromStackList(WCoreWindow *frame)
  569. {
  570.     int index = frame->stacking->window_level;
  571.  
  572.     if (XDeleteContext(dpy, frame->window, wStackContext)==XCNOENT) {
  573.     wwarning("RemoveFromStackingList(): window not in list ");
  574.     return;
  575.     }
  576.     /* remove from the window stack list */
  577.     if (frame->stacking->under)
  578.     frame->stacking->under->stacking->above = frame->stacking->above;
  579.     if (frame->stacking->above)
  580.     frame->stacking->above->stacking->under = frame->stacking->under;
  581.     else /* this was the first window on the list */
  582.     WMSetInBag(frame->screen_ptr->stacking_list, index,
  583.            frame->stacking->under);
  584.  
  585.     frame->screen_ptr->window_count--;
  586. }
  587.  
  588.  
  589. void
  590. ChangeStackingLevel(WCoreWindow *frame, int new_level)
  591. {
  592.     int old_level;
  593.  
  594.     if (frame->stacking->window_level == new_level)
  595.       return;
  596.     old_level = frame->stacking->window_level;
  597.  
  598.     RemoveFromStackList(frame);
  599.     frame->stacking->window_level = new_level;
  600.     AddToStackList(frame);
  601.     if (old_level > new_level) {
  602.     wRaiseFrame(frame);
  603.     } else {
  604.     wLowerFrame(frame);
  605.     }
  606. }
  607.